function matchInt(v) {
    match v {
        1 => printf("%s\n", "one");
        2 => printf("%s\n", "two");
        3 => printf("%s\n", "three");
        default => printf("%s\n", "default");
    }
}

function matchBool(b) {
    match b {
        true => printf("%s\n", "true");
        false => printf("%s\n", "false");
    }
}

function matchNoDefault(v) {
    match v {
        1 => printf("%s\n", "one");
        2 => printf("%s\n", "two");
    }
}

enum Fruit {
    Apple;
    Banana;
    Strawberry;
}

enum FruitQuantity {
    Apples(n: Int);
    Bananas(n: Int, m: Int);
    Grapes;
    Unknown;
    Boxes(i: Int, f: Fruit);
}

struct StructA {
    var a: Int;
    var b: CString;
    var c: StructB;
}

struct StructB {
    var d: Int;
    var e: CString;
}


function matchEnumSimple(e) {
    match e {
        Apple => printf("%s\n", "apple");
        Banana => printf("%s\n", "banana");
        default => printf("%s\n", "something else");
    }
}

function matchString(s) {
    match s {
        "apple" => printf("%s\n", "APPLE");
        "banana" => printf("%s\n", "BANANA");
        default => printf("%s\n", "something else");
    }
}

function matchFloat(v) {
    match v {
        1.5 => printf("%s\n", "1.5");
        2.5 => printf("%s\n", "2.5");
        default => printf("%s\n", "something else");
    }
}

function matchEnumComplex(v) {
    match v {
        Apples(1) => printf("an apple\n");
        Apples(n) => printf("%i apples\n", n);
        Bananas(1, m) => printf("a bunch of %i bananas\n", m);
        Bananas(n, m) => printf("%i bunches of %i bananas\n", n, m);
        Grapes => printf("some grapes\n");
        Boxes(1, Apple) => printf("a box of apples\n");
        Boxes(i, Apple) => printf("%i boxes of apples\n", i);
        Boxes(i, Banana) => printf("%i boxes of bananas\n", i);
        default => printf("???\n");
    }
}

function matchStruct(v) {
    match v {
        struct StructA {a: 1} => {
            printf("a is 1\n");
        }
        struct StructA {c: struct StructB {d: 3}} => {
            printf("d is 3\n");
        }
        struct StructA {a: 2, c: struct StructB {e: x}} =>
            printf("%s\n", x);
        _ => {
            printf("%s %s\n", v.b, v.c.e);
        }
    }
}

function main() {
    matchInt(0);
    matchInt(1);
    matchInt(2);
    matchInt(3);
    matchInt(4);

    matchBool(true);
    matchBool(false);

    match true {
        true => printf("%s\n", "match true: correct");
    }

    match false {
        false => printf("%s\n", "match false: correct");
    }

    match true {
        false => printf("%s\n", "match true: wrong");
    }

    match false {
        true => printf("%s\n", "match false: wrong");
    }

    match true {
        false => printf("%s\n", "wrong");
        default => printf("%s\n", "bool match: default");
    }

    match false {
        true => printf("%s\n", "wrong");
        default => printf("%s\n", "bool match: default");
    }

    matchNoDefault(1);
    matchNoDefault(2);
    matchNoDefault(3);

    matchEnumSimple(Apple);
    matchEnumSimple(Banana);
    matchEnumSimple(Strawberry);

    matchString("apple");
    matchString("banana");
    matchString("strawberry");

    matchFloat(1.5);
    matchFloat(2.5);
    matchFloat(2.6);

    matchEnumComplex(Apples(1));
    matchEnumComplex(Apples(3));
    matchEnumComplex(Bananas(1, 3));
    matchEnumComplex(Bananas(4, 5));
    matchEnumComplex(Grapes);
    matchEnumComplex(Unknown);
    matchEnumComplex(Boxes(1, Apple));
    matchEnumComplex(Boxes(2, Apple));
    matchEnumComplex(Boxes(2, Banana));
    matchEnumComplex(Boxes(2, Strawberry));

    matchStruct(struct StructA {
        a: 1,
        b: "hello",
        c: struct StructB {
            d: 3,
            e: "world",
        }
    });
    matchStruct(struct StructA {
        a: 2,
        b: "hello",
        c: struct StructB {
            d: 3,
            e: "world",
        }
    });
    matchStruct(struct StructA {
        a: 2,
        b: "nothing",
        c: struct StructB {
            d: 1,
            e: "hi",
        }
    });
    matchStruct(struct StructA {
        a: 3,
        b: "hello",
        c: struct StructB {
            d: 5,
            e: "world",
        }
    });
}
